package com.demo.microblockchain;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.demo.microblockchain.utils.StringUtil;

public class Block {

	public String hash; // 自己的数字签名
    public String previousHash; // 前一个区块哈希值
    public String merkleRoot; // 交易事务的merkle根是对区块中所有的交易事务进行哈希计算，构造一棵merkle数而得来，可以用于验证区块的完整性。
    public List<Transaction> transactions = new ArrayList<Transaction>(); // 交易事务
    public long timeStamp; // 区块打包时间戳
    public int nonce; // 随机目标值

    public Block(String previousHash) {
        this.previousHash = previousHash;
        this.timeStamp = new Date().getTime();
        // 所以每一个区块不仅包含前一个区块的hash值，同时包含自身的一个hash值，自身的hash值是通过之前的hash值和数据data通过hash计算出来的。
        // 如果前一个区块的数据一旦被篡改了，那么前一个区块的hash值也会同样发生变化（因为数据也被计算在内），这样也就导致了所有后续的区块中的hash值。
        // 所以计算和比对hash值会让我们检查到当前的区块链是否是有效的，也就避免了数据被恶意篡改的可能性，因为篡改数据就会改变hash值并破坏整个区块链。
        this.hash = calculateHash();
    }

    //Calculate new hash based on blocks contents
    public String calculateHash() {
        String calculatedhash = StringUtil.applySha256(
                previousHash +
                        Long.toString(timeStamp) +
                        Integer.toString(nonce) +
                        merkleRoot
        );
        return calculatedhash;
    }

    /**在比特币网络中所有的网络节点都分享了它们各自的区块链，然而最长的有效区块链是被全网所统一承认的，如果有人恶意来篡改之前的数据，然后创建一条更长的区块链并全网发布呈现在网络中，
     * 我们该怎么办呢？这就涉及到了区块链中另外一个重要的概念工作量证明，
     * 这里就不得不提及一下hashcash，这个概念最早来自于Adam Back的一篇论文，主要应用于邮件过滤和比特币中防止双重支付。
     *
     * 这里我们要求挖矿者做工作量证明，具体的方式是在区块中尝试不同的参数值直到它的hash值是从一系列的0开始的。
     * 让我们添加一个名为nonce的int类型以包含在我们的calculatehash（）方法中，以及需要的mineblock（）方法
     * @param difficulty
     */
    public void mineBlock(int difficulty) {
        merkleRoot = StringUtil.getMerkleRoot(transactions);
        String target = StringUtil.getDificultyString(difficulty); //Create a string with difficulty * "0"
        while (!hash.substring(0, difficulty).equals(target)) {
            nonce++;
            hash = calculateHash();
        }
        System.out.println("Block Mined!!! : " + hash);
    }

    //Add transactions to this block
    public boolean addTransaction(Transaction transaction) {
        //process transaction and check if valid, unless block is genesis block then ignore.
        if (transaction == null) return false;
        if ((previousHash != "0")) {
            if ((transaction.processTransaction() != true)) {
                System.out.println("Transaction failed to process. Discarded.");
                return false;
            }
        }

        transactions.add(transaction);
        System.out.println("Transaction Successfully added to Block");
        return true;
    }

	@Override
	public String toString() {
		return "Block [hash=" + hash + ", previousHash=" + previousHash
				+ ", merkleRoot=" + merkleRoot + ", transactions="
				+ transactions + ", timeStamp=" + timeStamp + ", nonce="
				+ nonce + "]";
	}

}
